import guizero
import requests
import threading
import hashlib
import os,pickle
import platform

class Setting:
    '设置项'
    id:int=None
    folder:str=None
class File:
    '文件对象'
    name:str
    path:str
    cTime:float
    mTime:float

    

class Myapp(guizero.App):
    def drawMyGui(self):
        if 'windows' in platform.system():
            self.os='w'
        else:
            self.os='u'
        self.tk.maxsize(400, 300)
        self.tk.minsize(200, 200)
        self.welcomeText=guizero.Text(self,'欢迎使用',height=5)
        self.signonBox = guizero.Box(self, layout='grid', border=2)
        guizero.Text(self.signonBox, text='用户名：', grid=[0, 0])
        guizero.TextBox(self.signonBox, grid=[1, 0])
        guizero.Text(self.signonBox, text='密码：', grid=[0, 1])
        guizero.TextBox(self.signonBox, grid=[1, 1])
        guizero.PushButton(self.signonBox, text='登录', command=self.signon, grid=[0, 2])
        guizero.PushButton(self.signonBox, text='注册', command=self.showSignin, grid=[1, 2])
        self.url = self.loadUrl()
        self.path=os.path.dirname(os.path.abspath(__file__))
        self.settings=self.loadsettings()
        self.files=[]
        app.display()
    def loadsettings(self):
        '加载设置项'
        settingsPath=os.path.join(self.path,'setup.dat')
        if os.path.isfile(settingsPath):
            with open(settingsPath,'rb') as f:
                return pickle.load(f)
        else:
            return {}



    def showSignin(self):
        '显示注册界面'
        newWindow = guizero.Window(self, title='注册页')
        newWindow.tk.maxsize(500, 500)
        box = guizero.Box(newWindow, layout='grid', border=2)
        guizero.Text(box, text='用户名：', grid=[0, 0])
        guizero.TextBox(box, grid=[1, 0])
        guizero.Text(box, text='密码：', grid=[0, 1])
        guizero.TextBox(box, grid=[1, 1])
        guizero.Text(box, text='重复密码：', grid=[0, 2])
        guizero.TextBox(box, grid=[1, 2])
        guizero.Text(box, text='邮箱/手机号：', grid=[0, 3])
        guizero.TextBox(box, grid=[1, 3])
        guizero.Text(box, text='邀请码：', grid=[0, 4])
        guizero.TextBox(box, grid=[1, 4])
        self.signinBox = box
        self.signinWindow = newWindow
        guizero.PushButton(newWindow, text='注册', command=self.signin)

    def loadUrl(self):
        '加载网址'
        return "http://127.0.0.1:8000/"

    def signin(self):
        '注册'
        texts = []
        chirdren = self.signinBox.children
        for i in range(len(chirdren)):
            if i % 2 == 1:
                texts.append(chirdren[i].value)

        if self.check(texts):
            texts[1] = self.encryptPasswd(texts[1])
            url = self.url +'signin/'
            params={
                'name':texts[0],
                'passwd':texts[1],
                'email':texts[3],
                'invitecode':texts[4]
            }
            request = requests.get(url,params=params)
            if request.status_code == 200:
                jsonData = request.json()
                if jsonData['status'] == 'yes':
                    guizero.info('注册成功', '恭喜你注册成功，返回登录!')
                    self.signinWindow.destroy()
                else:
                    guizero.info('注册失败', f'{jsonData["info"]}')
            else:
                guizero.info('注册失败', f'可能是网络问题！')

    def encryptPasswd(self, passwd):
        '对密码进行加密'
        m = hashlib.md5()
        m.update(passwd.encode())
        return m.hexdigest()

    def check(self, texts):
        '检查注册信息'
        ifTrue = 0
        for i in texts:
            if i == '':
                guizero.error('错误', '请完整填写注册信息！')
                break
        else:
            if texts[1] != texts[2]:
                guizero.error('错误', '两次密码输入不一致，请核实后输入！')
            else:
                ifTrue = 1
        return ifTrue

    def signon(self):
        '登录'
        chirldren=self.signonBox.children
        name=chirldren[1].value
        passwd=chirldren[3].value
        passwd=self.encryptPasswd(passwd)
        if name and passwd:
            params={
                'name':name,
                'passwd':passwd,
            } 
            url=self.url+'signon/'
            r=requests.get(url,params=params)
            if r.status_code==200:
                jsonData=r.json()
                if jsonData['status']=='yes':
                    guizero.info('登录成功','恭喜你登录成功！')
                    self.signonInfo=params                    
                    ID=jsonData['ID']
                    self.signonInfo['ID']=ID
                    if  ID in self.settings:
                        setting=self.settings[ID]                        
                    else:
                        setting=Setting()
                        setting.id=ID
                    self.signonBox.destroy()     
                    self.drawGuiAfterSingon(setting)              
                else:
                    guizero.error('错误',f'抱歉,{jsonData["info"]}')
            else:
                guizero.error('错误','抱歉，网络连接失败！')
        else:
            guizero.warn('警告','请完整填写登录信息')
    def drawGuiAfterSingon(self,setting):
        '登陆完的界面'
        self.welcomeText.value=f'欢迎{self.signonInfo["name"]}登录，你今天又变好看了！'
        self.tk.maxsize(400,600)
        self.height=600
        if setting.folder==None:
            folder=guizero.select_folder(title='同步文件夹未设置，请选择要设置的文件夹：')
            self.welcomeText.value+=f'\n当前同步文件夹:\n{folder}'
            if folder:
                if os.path.isdir(folder):
                    setting.folder=folder
                    self.settings[setting.id]=setting
                    settingsPath=os.path.join(self.path,'setup.dat')
                    with open(settingsPath,'wb') as f:
                        pickle.dump(self.settings,f)
                    self.infoTextBox=guizero.TextBox(self,width=41,height=24,multiline=True,scrollbar=True)
                    self.startSync()
                else:
                    guizero.warn('错误','同步文件夹选择错误，点击ok可以\n关掉本程序然后再启动本程序重新尝试即可。')
                    self.destroy()
            else:
                self.drawGuiAfterSingon(setting)
        else:
            self.startSync()
        
    def startSync(self):
        '开始同步数据'
        files=self.getFiles()
        r=requests.get(self.url+'getFilesList/',params=self.signonInfo)
        if r.status_code==200:
            cloudLists=r.json()
            tasks=self.createTasts(files,cloudLists['info'])
            self.startTasks(tasks)                   
            
        else:
            self.infoTextBox.value='可能是网络错误，获取失败'
    def getRelaPath(self,path):
        '获取相对目录'
        workpath=self.settings[self.signonInfo['ID']].folder
        nowDir=os.path.dirname(path)
        nowDir=nowDir.split(workpath)[1]            
        if nowDir:
            if self.os=='w':
                nowDir=nowDir.split('\\')
            else:
                nowDir=nowDir.split('/')
                nowDir=[str(i) for i in nowDir]
        else:
            nowDir='哎呀好奇怪'
        return nowDir
    def startTasks(self,tasks):
        '开始任务'
        upTasks=tasks['upload']
        downTasks=tasks['download']
        deleteTasks=tasks['delete']
        for nowFile in upTasks:
            fileName=nowFile['name']
            print(f'开始上传【{fileName}】文件...')
            path=nowFile['path']
            relaPath=self.getRelaPath(path)
            data={
                'name':self.signonInfo['name'],
                'passwd':self.signonInfo['passwd'],
                'fileName':fileName,
                'relaPath':relaPath,
            }
            files={                
                'file':(fileName,open(path,'r'))}
            r=requests.post(url=self.url+'upload/',data=data,files=files)
            if r.status_code==200:
                print(f'【{fileName}】上传完毕！')
            else:
                print('上传失败！')
        for nowFile in downTasks:
            params={
                'name':self.signonInfo['name'],
                'passwd':self.signonInfo['passwd'],
                'relaPath':nowFile['relaPath'],
                'fileName':nowFile['name']
            } 
            url=self.url+'download/'
            r=requests.get(url,params=params)
            path=self.settings[self.signonInfo['ID']].folder
            rPath=nowFile['relaPath']
            if rPath=='哎呀好奇怪':
                if self.os=='w':
                    rPath='\\'
                else:
                    rPath='/'
            else:
                if self.os=='w':
                    rPath='\\'.join(nowFile['relaPath'])
                    rPath+='\\'
                else:
                    rPath='/'.join(nowFile['relaPath'])
                    rPath+='/'
            path+=os.path.join(rPath,nowFile['name'])
            with open(path,'wb') as f:
                f.write(r.content)
            print(f"{nowFile['name']}下载完毕！")
        for nowFile in deleteTasks:
            params={
                'name':self.signonInfo['name'],
                'passwd':self.signonInfo['passwd'],
                'relaPath':nowFile['relaPath'],
                'fileName':nowFile['name']
            } 
            url=self.url+'delete/'
            r=requests.post(url,data=params)
            print(f"{nowFile['name']}删除成功！")


            
        
    def createTasts(self,files,cloudLists):
        '通过比较文件属性创建任务'
        tastks={
            'upload':[],
            'download':[],
            'delete':[],
        }
        for fileP in files:
            for fileS in cloudLists:
                fName=fileP['name']
                sName=fileS['name']
                fR=fileP['relaPath']
                sR=fileS['relaPath']
                fM=fileP['md5']
                sM=fileS['md5']
                if fName==sName and fR==sR:
                    if fM!=sM:
                        if fileP['mTime']>fileS['mTime']:#如果云上的修改时间和本地不一样
                            tastks['upload'].append(fileP)
                        elif fileP['mTime']<fileS['mTime']:
                            tastks['download'].append(fileP)
                    break                    
            else:
                tastks['upload'].append(fileP)
        for fileS in cloudLists:
            for fileP in files:
                if fileP['name']==fileS['name'] and fileP['relaPath']==fileS['relaPath']:
                    break
            else:
                tastks['delete'].append(fileS)

        return tastks
        


    def getFiles(self):
        '获取文件属性'
        files=[]
        for filepath,dirnames,filenames in os.walk(self.settings[self.signonInfo['ID']].folder):
            for filename in filenames:
                path=os.path.join(filepath,filename)
                with open(path, 'rb') as fp:
                    data = fp.read()
                    md5=hashlib.md5(data).hexdigest()
                cTime=os.path.getctime(path)
                mTime=os.path.getmtime(path)
                files.append({
                    'name':filename,
                    'path':os.path.join(filepath,filename),
                    'relaPath':self.getRelaPath(os.path.join(filepath,filename)),
                    'md5':md5 ,
                    'cTime':cTime,
                    'mTime':mTime,                 
                })
        return files


if __name__ == "__main__":
    app = Myapp('airCloud', 400, height=600, bg='white')
    app.drawMyGui()
